package cn.com.bluemoon.daps.sync.core.sync.compare.sql;

import cn.com.bluemoon.daps.common.enums.SyncToEnv;
import cn.com.bluemoon.daps.common.toolkit.BmAssetUtils;
import cn.com.bluemoon.daps.model.entity.DapDataModelTable;
import cn.com.bluemoon.daps.model.entity.DapDataModelTableField;
import cn.com.bluemoon.daps.sync.base.BmScript;
import cn.com.bluemoon.daps.sync.base.ModifyType;
import cn.com.bluemoon.daps.sync.base.SqlCompareResult;
import cn.com.bluemoon.daps.sync.core.sync.GlobalConfig;
import cn.com.bluemoon.daps.sync.core.sync.Tuple2;
import cn.com.bluemoon.daps.sync.core.sync.compare.FieldComparator;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.List;
import java.util.Optional;

/**
 * @author Jarod.Kong
 */
@Slf4j
public class SqlFieldComparator extends FieldComparator<SqlCompareResult> {
    public SqlFieldComparator(GlobalConfig globalConfig, Tuple2<DapDataModelTable> tableTuple2) {
        super(globalConfig, tableTuple2);
    }

    @Override
    public void change0(List<SqlCompareResult> sqlCollector) {
        super.change0(sqlCollector);
        // 2021/10/27 优化排序rank值问题
        // 分析 1）只移动了一个字段 2）移动了多个字段 3）移动了一个修改过的字段
        final String preFieldName = currFieldNameAfterFieldName(getNewTFieldsOrderByFieldRank(), getNewT());
        BmScript sql = bmSql.changeField(newTable, getOldT(), getNewT(), preFieldName);
        sqlCollector.add(SqlCompareResult.builder()
                .modifyType(ModifyType.ALTER.field())
                .script(sql)
                .build());
    }

    /**
     * 根据 newT当前的字段获取排序后的前一个字段名，用于after xx
     *
     * @return preField
     */
    protected String currFieldNameAfterFieldName(List<DapDataModelTableField> fields, DapDataModelTableField newTCurrent) {
        // 增加前一个字段信息，用于指定当前字段的位置
        BmAssetUtils.isTrue(!fields.isEmpty(), "字段列表不可为空");
        String preFieldName = null;
        // ！首个
        if (!fields.get(0).equals(newTCurrent)) {
            DapDataModelTableField preField = fields.get(0);
            DapDataModelTableField cur;
            for (int i = 1; i < fields.size(); i++) {
                cur = fields.get(i);
                if (newTCurrent.equals(cur)) {
                    break;
                }
                preField = cur;
            }
            preFieldName = preField.getFieldName();
        }
        return preFieldName;
    }

    @Override
    public void drop0(List<SqlCompareResult> sqlCollector) {
        // 针对模型没有而数据库有的表，目前需求是忽略数据库的表，而不进行删除表+字段+索引
        if (!globalConfig.isCanDropDbTable() && oldTable != null && newTable == null) {
            log.debug("开发同步中：针对模型没有而数据库有的表，目前需求是忽略数据库的表，而不进行删除表字段，table:{}", oldTable);
        } else if (oldTable != null && newTable != null) {// 要是删除表，字段就忽略删除，否则会找不到表；要是修改字段，则进行生产语句
            super.drop0(sqlCollector);
            DapDataModelTableField oldField = getOldT(); // 库字段为oldField，去模型表字段中招oldFieldName
            if (isMatchIgnoreDropField(oldField)) {
                return;
            }
            BmScript sql = bmSql.dropField(oldTable, oldField);
            SqlCompareResult res = SqlCompareResult.builder()
                    .modifyType(ModifyType.DROP.field())
                    .script(sql)
                    .build();
            sqlCollector.add(res);
        }
    }

    /**
     * 2021/10/27 增加业务逻辑：发现该字段需要进行删除，则尝试去”模型表字段变化“中溯源，该字段的原字段，若源字段在库中存在则生成修改字段语句（modify oldFiled newField),若库中没有，则删除
     */
    private boolean isMatchIgnoreDropField(DapDataModelTableField oldField) {
        boolean matchIgnoreDropField = false;
        if (SyncToEnv.DEV.equals(globalConfig.getSyncToEnv())) {
            String oldFieldName = oldField.getFieldName();// 库字段没有所谓的fieldName,oldFieldName，都是fieldName
            // 如果旧字段（当前库表字段）的旧字段与库表中的字段对应，则判定为该字段属于修改
            List<DapDataModelTableField> newTFieldsOrderByFieldRank = getNewTFieldsOrderByFieldRank();
            Optional<DapDataModelTableField> matchIgnore = newTFieldsOrderByFieldRank.stream().filter(d -> StringUtils.equalsIgnoreCase(oldFieldName, d.getOldFieldName())).findFirst();
            if (matchIgnore.isPresent()) {
                DapDataModelTableField f = matchIgnore.get();
                log.info("模型表{}发现字段对比中，{}为删除，业务上发现模型中为修改字段id={},oldFieldName={}", oldTable.getEngName(), oldFieldName, f.getId(), f.getOldFieldName());
                matchIgnoreDropField = true;
            }
        }
        return matchIgnoreDropField;
    }

    @Override
    public void create0(List<SqlCompareResult> sqlCollector) {
        if (oldTable == null && newTable != null) {
            log.debug("新建表，忽略创建表的创建字段sql，统一有SqlTableComparator#create0生产, table:{}", newTable);
            return;
        }
        super.create0(sqlCollector);
        assert newTable != null;
        // 如果新字段（当前模型字段）的旧字段与库表中的字段对应，则判定为该字段属于修改
        DapDataModelTableField newField = getNewT();
        String oldFieldName = newField.getOldFieldName();
        SyncToEnv syncToEnv = globalConfig.getSyncToEnv();
        SqlCompareResult res;
        if (SyncToEnv.DEV.equals(syncToEnv)) {
            List<DapDataModelTableField> oldTFieldsOrderByFieldRank = getOldTFieldsOrderByFieldRank();
            res = doRenameFieldOnCreateField(newField, oldFieldName, oldTFieldsOrderByFieldRank)
                    .orElseGet(() -> doCreateField(newField));
        } else {
            res = doCreateField(newField);
        }
        sqlCollector.add(res);

    }

    private Optional<SqlCompareResult> doRenameFieldOnCreateField(DapDataModelTableField newField, String oldFieldName, List<DapDataModelTableField> oldTFieldsOrderByFieldRank) {
        return oldTFieldsOrderByFieldRank.stream()
                .filter(d -> StringUtils.equalsIgnoreCase(oldFieldName, d.getFieldName()))
                .findFirst().map(oldField -> {
                    BmScript sql = bmSql.changeField(newTable, oldField, newField, currFieldNameAfterFieldName(getNewTFieldsOrderByFieldRank(), newField));
                    return SqlCompareResult.builder()
                            .modifyType(ModifyType.ALTER.field())
                            .script(sql)
                            .build();
                });
    }

    private SqlCompareResult doCreateField(DapDataModelTableField newField) {
        final String preFieldName = currFieldNameAfterFieldName(getNewTFieldsOrderByFieldRank(), newField);
        BmScript sql = bmSql.createField(newTable, newField, preFieldName);
        return SqlCompareResult.builder()
                .modifyType(ModifyType.CREATE.field())
                .script(sql)
                .build();
    }
}
